/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include <common.h>
#include <os/irq.h>
#include <os/mm.h>
#include <os/sched.h>
#include <screen.h>
#include <sbi.h>
#include <stdio.h>
#include <os/time.h>
#include <os/syscall.h>
// #include <os/futex.h>
#include <os/binsem.h>
#include <test.h>

#include <csr.h>

extern void ret_from_exception();
extern void printk_task1(void);
extern void __global_pointer$();

static void init_pcb_stack(
    ptr_t kernel_stack, ptr_t user_stack, ptr_t entry_point,
    pcb_t *pcb)
{
    regs_context_t *pt_regs =
        (regs_context_t *)(kernel_stack - sizeof(regs_context_t));
    ptr_t new_ksp;
    /* TODO: initialization registers
     * note: sp, gp, ra, sepc, sstatus
     * gp should be __global_pointer$
     * To run the task in user mode,
     * you should set corresponding bits of sstatus(SPP, SPIE, etc.).
     */
    for(size_t i=0; i<32; i++){
      pt_regs->regs[i]=0;
    }
    new_ksp = kernel_stack - sizeof(regs_context_t);
    pcb->kernel_sp = new_ksp;
    pt_regs->regs[2] = user_stack;
    pt_regs->regs[3] = (reg_t)__global_pointer$;
    pt_regs->regs[4] = (reg_t)pcb;
    pt_regs->sbadaddr = 0;
    pt_regs->scause = 0;
    pt_regs->sepc = entry_point;	
    pt_regs->sstatus = SR_SPIE;

    // set sp to simulate return from switch_to
    /* TODO: you should prepare a stack, and push some values to
     * simulate a pcb context.
     */
}

static void init_pcb()
{
     /* initialize all of your pcb and add them into ready_queue
     * TODO:
     */
    init_list_head(&ready_queue);				 //��ʼ��˫������
    init_list_head(&timers);
    struct task_info *task = NULL;
    for (size_t i = 1; i <= num_timer_tasks + num_sched2_tasks + num_lock2_tasks; i++)
    {
      if(i <= num_timer_tasks)
        task = timer_tasks[i - 1];
      else if(i <= num_sched2_tasks + num_timer_tasks)
        task = sched2_tasks[i - num_timer_tasks - 1];
      else
        task = lock2_tasks[i - num_timer_tasks - num_sched2_tasks -1];
    
  		pcb[i].kernel_sp = allocPage(1) + PAGE_SIZE;
  		pcb[i].user_sp = allocPage(1) + PAGE_SIZE;
      pcb[i].type = task->type;
  		init_pcb_stack(pcb[i].kernel_sp, pcb[i].user_sp, task->entry_point, &pcb[i]);
  		pcb[i].pid = i;
  		pcb[i].status = TASK_READY;
      pcb[i].preempt_count = 0; //记录屏蔽中断的次数，使能中断时依次打开
      pcb[i].task_priority = task->priority;
      pcb[i].priority = task->priority;
      list_add_prior(&pcb[i].list);
	  }
    /* remember to initialize `current_running`
     * TODO:
     */
    current_running = &pid0_pcb;
}

static void init_syscall(void)
{
    // initialize system call table.
    for(size_t i = 0; i < NUM_SYSCALLS; i++)
		  syscall[i] = NULL;
	  syscall[SYSCALL_SLEEP] = (long (*)())&do_sleep;
	  syscall[SYSCALL_BINSEMGET] = (long (*)())&get_binsem;
	  syscall[SYSCALL_BINSEMOP] = (long (*)())&op_binsem;
	  syscall[SYSCALL_WRITE] = (long (*)())&screen_write;
	  syscall[SYSCALL_CURSOR] = (long (*)())&screen_move_cursor;
	  syscall[SYSCALL_REFLUSH] = (long (*)())&screen_reflush;
	  syscall[SYSCALL_GET_TIMEBASE] = (long (*)())&get_time_base;
	  syscall[SYSCALL_GET_TICK] = (long (*)())&get_ticks;
}

// jump from bootloader.
// The beginning of everything >_< ~~~~~~~~~~~~~~
int main()
{
    // init Process Control Block (-_-!)
    init_pcb();
    printk("> [INIT] PCB initialization succeeded.\n\r");

    // read CPU frequency
    time_base = sbi_read_fdt(TIMEBASE);
	
    // init futex mechanism
    // init_system_futex();
    init_binsems();

    // init interrupt (^_^)
    init_exception();
    printk("> [INIT] Interrupt processing initialization succeeded.\n\r");

    // init system call table (0_0)
    init_syscall();
    printk("> [INIT] System call initialized successfully.\n\r");

    // fdt_print(riscv_dtb);

    // init screen (QAQ)
    init_screen();
    printk("> [INIT] SCREEN initialization succeeded.\n\r");

    // TODO:
    // Setup timer interrupt and enable all interrupt
    reset_irq_timer();

    while (1) {
        // (QAQQQQQQQQQQQ)
        // If you do non-preemptive scheduling, you need to use it
        // to surrender control do_scheduler();
        enable_interrupt();
        __asm__ __volatile__("wfi\n\r":::);
        //do_scheduler();
    };
    return 0;
}
